package be.vdab.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import be.vdab.entities.Member;
import be.vdab.entities.Voorstelling;
import be.vdab.util.GelukteMislukteVoorstellingen;


public class ReadSqlDAO extends AbstractDAO {
	private static final String GET_ALL_GENRES_SQL = "select naam from genres order by naam";
	private static final String GET_ALL_MEMBERS_SQL = "select * from klanten";
	private static final String GET_ALL_VOORSTELLINGEN_SQL = "select * from voorstellingen inner join genres"
			+ " on voorstellingen.genrenr = genres.genrenr"
			+ " where genres.naam = ?";
	private static final String GET_ONE_VOORSTELLING_SQL = "select * from voorstellingen where voorstellingen.VoorstellingsNr = ?";
	private static final String ADD_ONE_KLANT_SQL = "insert into klanten(Voornaam,Familienaam,Straat,HuisNr,Postcode,Gemeente,GebruikersNaam,Paswoord) values (?,?,?,?,?,?,?,?)";
	private static final String INSERT_RESERVATIE_SQL = "insert into reservaties(klantnr,voorstellingsnr,plaatsen) values(?,?,?)";
	private static final String UPDATE_VOORSTELLING_SQL = "update voorstellingen set vrijeplaatsen=vrijeplaatsen-? where voorstellingsnr=? and vrijeplaatsen>=?";

	public List<String> findGenres() {
		try (Connection connection = getConnection();
				Statement statement = connection.createStatement();
				ResultSet resultSet = statement
						.executeQuery(GET_ALL_GENRES_SQL);) {
			List<String> genres = new ArrayList<>();
			while (resultSet.next()) {
				genres.add(resultSetRijNaarString(resultSet));
			}
			return genres;
		} catch (SQLException ex) {
			throw new DAOException("Kan genres niet lezen uit database", ex);
		}
	}

	private String resultSetRijNaarString(ResultSet resultSet)
			throws SQLException {
		return resultSet.getString("naam");
	}

	public List<Voorstelling> findVoorstellingen(String naam) {
		try (Connection connection = getConnection();
				PreparedStatement statement = connection
						.prepareStatement(GET_ALL_VOORSTELLINGEN_SQL);) {
			List<Voorstelling> voorstelling = new ArrayList<>();
			statement.setString(1, naam);
			try (ResultSet resultSet = statement.executeQuery();) {
				while (resultSet.next()) {
					voorstelling.add(resultSetRijNaarVoorstelling(resultSet));
				}
			}

			return voorstelling;
		} catch (SQLException ex) {
			throw new DAOException(
					"Kan voorstellingen niet lezen uit database", ex);
		}

	}

	private Voorstelling resultSetRijNaarVoorstelling(ResultSet resultSet)
			throws SQLException {
		Voorstelling voorstelling = new Voorstelling();
		voorstelling.setGenreNr(resultSet.getInt("GenreNr"));
		voorstelling.setTitel(resultSet.getString("Titel"));
		voorstelling.setUitvoerders(resultSet.getString("Uitvoerders"));
		voorstelling.setPrijs(resultSet.getBigDecimal("Prijs"));
		voorstelling.setVrijePlaatsen(resultSet.getInt("VrijePlaatsen"));
		voorstelling.setVoorstellingsNr(resultSet.getInt("VoorstellingsNr"));
		voorstelling.setDatum(resultSet.getString("Datum"));
		return voorstelling;
	}

	public Voorstelling findOneVoorstelling(int voorstellingsNr) {
		try (Connection connection = getConnection();
				PreparedStatement statement = connection
						.prepareStatement(GET_ONE_VOORSTELLING_SQL);) {
			Voorstelling voorstelling = null;
			statement.setInt(1, voorstellingsNr);
			try (ResultSet resultSet = statement.executeQuery();) {
				while (resultSet.next()) {
					voorstelling = resultSetRijNaarVoorstelling(resultSet);
				}
			}

			return voorstelling;
		} catch (SQLException ex) {
			throw new DAOException(
					"Kan voorstellingen niet lezen uit database", ex);
		}

	}

	public Member getMember(String gebruiker, String paswoord) {
		Member member = null;
		try (Connection connection = getConnection();
				Statement statement = connection.createStatement();
				ResultSet resultSet = statement
						.executeQuery(GET_ALL_MEMBERS_SQL);) {
			while (resultSet.next()) {
				String naam = resultSet.getString("GebruikersNaam");
				if (naam.equals(gebruiker)) {
					String code = resultSet.getString("paswoord");
					if (code.equals(paswoord)) {
						member = makeMember(resultSet);
					}
				}
			}

		} catch (SQLException ex) {
			throw new DAOException("Kan genres niet lezen uit database", ex);
		}

		return member;
	}

	public Member makeMember(ResultSet resultSet) throws SQLException {
		Member member = new Member();
		member.setKlantNr(resultSet.getInt("KlantNr"));
		member.setHuisNr(resultSet.getString("HuisNr"));
		member.setPostcode(resultSet.getString("Postcode"));
		member.setVoornaam(resultSet.getString("Voornaam"));
		member.setFamilienaam(resultSet.getString("Familienaam"));
		member.setStraat(resultSet.getString("Straat"));
		member.setGemeente(resultSet.getString("Gemeente"));
		member.setGebruikersNaam(resultSet.getString("GebruikersNaam"));
		member.setPaswoord(resultSet.getString("Paswoord"));
		return member;
	}

	public boolean isReedsGebruikersNaam(String gebruikersNaam) {
		boolean booleanValue = false;
		try (Connection connection = getConnection();
				Statement statement = connection.createStatement();
				ResultSet resultSet = statement
						.executeQuery(GET_ALL_MEMBERS_SQL);) {
			while (resultSet.next()) {
				String naam = resultSet.getString("GebruikersNaam");
				if (naam.equals(gebruikersNaam)) {
					booleanValue = true;
				}
			}

		} catch (SQLException ex) {
			throw new DAOException("Kan genres niet lezen uit database", ex);
		}

		return booleanValue;
	}

	public void addMember(Member member) {
		try (Connection connection = getConnection();
				PreparedStatement statement = connection.prepareStatement(
						ADD_ONE_KLANT_SQL, Statement.RETURN_GENERATED_KEYS);) {
			statement.setString(1, member.getVoornaam());
			statement.setString(2, member.getFamilienaam());
			statement.setString(3, member.getStraat());
			statement.setString(4, member.getHuisNr());
			statement.setString(5, member.getPostcode());
			statement.setString(6, member.getGemeente());
			statement.setString(7, member.getGebruikersNaam());
			statement.setString(8, member.getPaswoord());
			statement.executeUpdate();
			try (ResultSet resultSet = statement.getGeneratedKeys();) {
				if (!resultSet.next()) {
					throw new DAOException(
							"Kan nummer toegevoegde klant niet lezen uit database");
				}
				member.setKlantNr(resultSet.getLong(1));
			}
		} catch (SQLException ex) {
			throw new DAOException("Kan klant niet toevoegen aan database", ex);
		}

	}

	@SuppressWarnings("finally")
	public GelukteMislukteVoorstellingen reserveer(Map<Integer, Integer> mandje, int klantNr) {
        Connection connection = null;
        PreparedStatement voorstellingStatement = null;
        PreparedStatement reservatieStatement = null;
        GelukteMislukteVoorstellingen gelukteMislukteVoorstellingen = new GelukteMislukteVoorstellingen();
        try {
            connection = getConnection();
            voorstellingStatement = connection.prepareStatement(UPDATE_VOORSTELLING_SQL);
            reservatieStatement = connection.prepareStatement(INSERT_RESERVATIE_SQL);
            int voorstellingsNr;
            Integer plaatsen;
            for (Map.Entry<Integer, Integer> mandjeEntry : mandje.entrySet()) {
                // begin transactie:
                connection.setAutoCommit(false);
                voorstellingsNr = mandjeEntry.getKey();
                plaatsen = mandjeEntry.getValue();
                voorstellingStatement.setInt(1, plaatsen);
                voorstellingStatement.setInt(2, voorstellingsNr);
                voorstellingStatement.setInt(3, plaatsen);
                if (voorstellingStatement.executeUpdate() == 1) {
                    reservatieStatement.setInt(1, klantNr);
                    reservatieStatement.setInt(2, voorstellingsNr);
                    reservatieStatement.setInt(3, plaatsen);
                    reservatieStatement.executeUpdate();
                    connection.commit();
                    gelukteMislukteVoorstellingen.addGelukteVoorstelling(voorstellingsNr, plaatsen);
                } else {
                    connection.rollback();
                    gelukteMislukteVoorstellingen.addMislukteVoorstelling(voorstellingsNr, plaatsen);
                }
            }
        } catch (SQLException ex) {
            try {
                connection.rollback();
            } catch (SQLException sqlEx) {
                throw new DAOException("rollback mislukt", sqlEx);
            } finally {
                throw new DAOException("Unable to  reserveer", ex);
            }
        } finally {
            close(voorstellingStatement, reservatieStatement, connection);
        }
        return gelukteMislukteVoorstellingen;
    }

}
